package fr.zombieGame;

/**
 * Polytech' Nice - Sophia Antipolis / SI3 / POO2
 * Annee 2011_2012
 * 
 * OMG Zombies !
 * 
 * Classe Personnage : modelisation d'un personnage
 * 
 * @author J. Harnetiaux et J. Lionne
 */

import java.util.Iterator;
import java.util.List;
import fr.comportement.*;
import com.simulator.*;

public abstract class Personnage {

    protected String nom;
    protected int pointsDeVie;
    protected int force;
    protected boolean estVivant;

    protected Location location;
    protected Field field;

    protected Deplacement deplacement;
    protected EspritCombatif espritCombatif;
    protected Soutien soutien;

    /**
     * Premier constructeur normal
     * Applique un comportement par defaut
     * @param nom
     *          Nom du personnage
     * @param pointsDeVie
     *          Nombre de points de vie initial du personnage
     */
    public Personnage(String nom, int pointsDeVie, Field field, Location location) {
        // Initialiser les attributs
        //
        this.nom = nom;
        this.pointsDeVie = pointsDeVie;
        this.estVivant = true;
        this.field = field;
        setLocation(location);

        // Appliquer un comportement par defaut
        //
        this.deplacement = new Marcher();
        this.espritCombatif = new Pacifiste();
        this.soutien = new PasDeSoutien();
        this.estVivant =true;
    }

    /**
     * Second constructeur normal
     * Applique le comportement definit par les parametres
     * @param nom
     *          Nom du personnage
     * @param pointsDeVie
     *          Nombre de points de vie initial du personnage
     * @param deplacement
     *          Strategie de deplacement
     * @param espritCombatif
     *          Strategie de combat
     * @param soutient
     *          Strategie de soutient
     */
    public Personnage(String nom, int pointsDeVie, Field field, Location location,
            Deplacement deplacement, EspritCombatif espritCombatif, Soutien soutien) {
        this(nom, pointsDeVie, field, location);
        this.deplacement = deplacement;
        this.espritCombatif = espritCombatif;
        this.soutien = soutien;
    }

    /**
     * Accesseur
     * @return le nom du personnage
     */
    public String getNom() {
        return nom;
    }

    /**
     * Accesseur
     * @return le nombre de points de vie du personnage
     */
    public int getPointsDeVie() {
        return pointsDeVie;
    }
    
    /**
     * Accesseur
     * @return la force du personnage
     */
    public int getForce() {
        return force;
    }

    /**
     * Accesseur
     * @return true si le personnage est vivant. False sinon
     */
    public boolean estVivant() {
        return estVivant;
    }

    /**
     * Accesseur
     * @return l'instance field associee au personnage
     */
    public Field getField() {
        return field;
    }

    /**
     * Accesseur
     * @return la localisation du personnage sur la carte
     */
    public Location getLocation() {
        return location;
    }

    /**
     * Accesseur
     * @return le comportement de deplacement du personnage
     */
    public Deplacement getDeplacement() {
        return deplacement;
    }

    /**
     * Accesseur
     * @return le comportement de combat du personange
     */
    public EspritCombatif getEspritCombatif() {
        return espritCombatif;
    }

    /**
     * Accesseur
     * @return le comportement de soutien du personnage
     */
    public Soutien getSoutien() {
        return soutien;
    }

    /**
     * @param estVivant
     *          true si le personnage est vivant. False sinon
     */
    public void setMort() {
        estVivant = false;
        pointsDeVie = 0;
        if (location != null) {
            field.clear(location);
            location = null;
            field = null;
        }
    }

    /**
     * @param deplacement
     *          Nouvelle strategie de deplacement
     */
    public void setDeplacement(Deplacement deplacement) {
        this.deplacement = deplacement;
    }

    /**
     * @param espritCombatif
     *          Nouvelle strategie de combat
     */
    public void setEspritCombatif(EspritCombatif espritCombatif) {
        this.espritCombatif = espritCombatif;
    }

    /**
     * @param soutient
     *          Nouvelle strategie de soutient
     */
    public void setSoutien(Soutien soutien) {
        this.soutien = soutien;
    }

    /**
     * @param newLocation
     *          Change la localisation du personnage
     */
    public void setLocation(Location newLocation) {
        if (location != null) {
            field.clear(location);
        }
        location = newLocation;
        field.place(this, newLocation);
    }

    /**
     * Reduit le nombre de points de vie du personnage
     * @param reduction
     *          Reduction a appliquer au points de vie
     */
    public void reduirePointsDeVie(int reduction) {
        pointsDeVie = pointsDeVie - reduction;
        if (pointsDeVie < 0) {
            pointsDeVie = 0;
            setMort();
        }
    }


    /**
     * Augmente le nombre de points de vie du personnage
     * @param augmentation
     *          Augmentation appliquer au points de vie
     */
    public void  augmenterPointsDeVie( int augmentation) {
        pointsDeVie = pointsDeVie + augmentation;
    }

    /**
     * Affiche a l'ecran ce que va dire un personnage
     * @param str
     *          Ce que va dire le personnage
     */
    public void crier(String str) {
        System.out.println(nom + " dit : " + str);
    }    

    /**
     * Methode declanchee lorsque le personnage rencontre un autre personnage
     * @param perso
     *          Personnage rencontre
     */
    protected void rencontrerPersonnage(Personnage perso) {
        // Appliquer la strategie de combat si le personnage est de classe differente
        // Sinon, appliquer la strategie de soutient
        //
        if(!perso.getClass().getName().equals(this.getClass().getName())) espritCombatif.combat(this, perso, force);
        else soutien.soutenir(perso);
    }

    /**
     * Le personnage va regarder s'il y a un autre personnage autour de lui
     * et appliquer la methode rencontrerPersonnage sur le premier personnage rencontre.
     * @param location
     *            Ou se trouve le personnage
     * @return l'endroit ou le personnage a ete trouve, null s'il n'y avait personne
     */
    protected Location trouverPersonnage(Location location) {
        List<Location> adjacent = field.adjacentLocations(location);
        Iterator<Location> it = adjacent.iterator();
        while (it.hasNext()) {
            Location loc = it.next();
            Object perso = field.getObjectAt(loc);
            if (perso instanceof Personnage) {
                rencontrerPersonnage((Personnage)perso);
            }
        }
        return null;
    }

    /**
     * Trouve un ennemi dans les localisations disponibles
     * @param location
     *          Localisation du personnage
     * @return
     *          La localisation de l'ennemi
     */
    protected Location trouverEnnemi(Location location) {
        List<Location> available = field.availableLocations(location, deplacement.getVitesse());
        Iterator<Location> it = available.iterator();
        while (it.hasNext()) {
            Location where = it.next();
            Object perso = field.getObjectAt(where);
            if (perso != null && !this.getClass().getName().equals(perso.getClass().getName())) {
                Personnage ennemi = (Personnage) perso;
                if (ennemi.estVivant()) {
                    Location adjacent = field.freeAdjacentLocation(where);
                    if(adjacent == null) continue;
                    this.espritCombatif.combat(this, ennemi, force);
                    return adjacent;
                }
            }
        }
        return null;
    }
    
    /**
     * Applique la fin d'un tour pour le perso
     */
    protected void appelerFinDeTour() {
        if(estVivant) finDeTour();
    }

    /**
     * Methode declanchee a chaque fin de tour pour chaque personnage
     */
    public abstract void finDeTour();
}
